package tjs.study.notes.dotest.jvm.thread;

import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tjs.study.notes.base.common.ThreadUtils;

import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * Created by Nicky.Tang on 2023/11/27 10:00
 *
 * @since 02.12.10
 */
public class 测试可见性 {
    private static final Logger logger = LoggerFactory.getLogger(测试可见性.class);
    public static int serviceNum = 1;

    public static void main(String[] args) throws InterruptedException {
        /**
         * add时加锁
         * volatile修饰保证，保证可见性：{@link CopyOnWriteArrayList#array}
         */
        // CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
        // copyOnWriteArrayList.add("")
        
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        ServiceManager serviceManager = new ServiceManager();

        for (Integer i = 0; i < 20; i++) {
            Integer finalI = i;
            executorService.submit(() -> {
                Instance instance = new Instance("127.0.0." + finalI, finalI);
                Service lastService = serviceManager.putIfAbsentService(new Service(RandomUtils.nextInt(0, serviceNum)));
                 synchronized (lastService) {
                    // lastService.putDirectInstance(instance); // 只要加了synchronized锁，不使用Copy-On-Write也能保证可见性
                    lastService.putInstance(instance);
                    logger.info("{} 追加实例：{}", lastService.name, instance);
                 }
            });
        }

        new Thread(() -> {
            while (true) {
                for (int i = 0; i < serviceNum; i++) {
                    Service lastService = serviceManager.putIfAbsentService(new Service(i));
                    // synchronized (lastService) { // 读取的时候加锁就会阻塞。只要写入时加了sync锁，就能保证可见性
                    logger.info("{} 可见实例：{}", lastService.name,
                            lastService.ephemeralInstances.stream()
                                    .sorted(Comparator.comparing(item -> item.port))
                                    .map(item -> String.valueOf(item.port)).collect(Collectors.joining(",")));
                    // }
                }
                ThreadUtils.sleepMilliSeconds(500);
            }
        }).start();
        executorService.awaitTermination(9999, TimeUnit.MINUTES);
    }

    public static class Instance {
        String ip;
        int port;

        public Instance(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }

        @Override
        public String toString() {
            return "Instance{" +
                    "ip='" + ip + '\'' +
                    ", port=" + port +
                    '}';
        }
    }

    public static class Service {
        String name;
        private Set<Instance> ephemeralInstances = new HashSet<>();

        public Service(int name) {
            this.name = String.valueOf(name);
        }

        public void putDirectInstance(Instance instance) {
            this.ephemeralInstances.add(instance);
            ThreadUtils.sleepMilliSeconds(500);
        }

        public void putInstance(Instance instance) {
            Set<Instance> ephemeralInstancesCopy = new HashSet<>(ephemeralInstances);
            ephemeralInstancesCopy.add(instance);
            ephemeralInstances = ephemeralInstancesCopy;
            ThreadUtils.sleepMilliSeconds(500);
        }
    }

    public static class ServiceManager {
        private final Map<String, Service> serviceMap = new ConcurrentHashMap<>();

        public Service putIfAbsentService(Service service) {
            return serviceMap.computeIfAbsent(service.name, k -> service);
        }

    }
}
